/*!
 * vinculum
 * Copyright(c) 2017 azzly
 * MIT Licensed
 */
 
'use strict';

/**
 * Module dependencies.
 * @private
 */

const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const http = require('http');
const https = require('https');

/**
 * Variables
 * @private
 */

/**
 * prototype.
 */

const obj = exports = module.exports = {};

/**
 * http request
 *
 * @public
 */
 
obj.http = function(options,data,ishttps){
	let resdata = [];
	let protol = http;
	if(ishttps) {
		protol = https;
		if(!options.port) options.port = 443;
	}else{
		if(!options.port) options.port = 80;
	}
	
	if(!options.headers) options.headers = {};
	
	if(!options.metod){
		if(data){
			options.metod = 'POST';
			if(!options.headers['Content-Length']){
				options.headers['Content-Length'] = Buffer.from(data,'utf8').length;
			}
		}else{
			options.metod = 'GET';
		}
	}
	
	return new Promise((resolve, reject)=>{
		const req = protol.request(options, (res)=>{
			if(options.method === 'HEAD'){
				resolve(res.headers);
			}else if(options.fileWriteStream){
				res.pipe(options.fileWriteStream);
				res.on('end',()=>{
					options.fileWriteStream.end();
					options.fileWriteStream = null;
					resolve(res.headers);
				});
			}else{
				res.on('data', (d)=>{
					resdata.push(d);
				});
				res.on('end',()=>{
					resolve({
						body:	Buffer.concat(resdata)
						,headers: res.headers
					});
				});
			}
		});
		
		req.on('error', (e)=>{
			if(options.method !== 'HEAD' || e.message !== 'Parse Error'){
				reject(e.message); 
			}
		});
		
		if(data){
			req.write(data);
		}	
		req.end();
	});
};
 
/**
 * https request
 *
 * @public
 */
 
obj.https = function(options,data){
  return obj.http(options,data,true);
};

/**
 * async.
 *
 * @public
 */

obj.async = function(arr,fun,callback){
	const one = arr.shift();
	if(one){
		fun(one,e=>{
			if(e){
				callback(e);
			}else{
				obj.async(arr,fun,callback);
			}
		});
	}else{
		callback(null);
	}
};

/**
 *	stringify.
 * @public
 **/
 
obj.stringify = function(a){
	return obj.json2string(a);
};

/**
 * uuid.
 * @public
 **/
 
obj.uuid = function(format){
	let str1 = format || (new Array(32)).join('x');
	let str2 = str1.replace(/[xy]/g,function(c){
		let r = Math.random()*16 | 0;
		let v = (c == 'x' ? r : (r&0x3| 0x8));
		let isu = Math.random() > 0.5;
		if(isu){
			return v.toString(16).toUpperCase();
		}else{
			return v.toString(16);
		}
	});
	return str2;
};

/**
 * extend.
 * @public
 **/

obj.extend = function(...args){
	if(args.length === 2){
		let a = args[0] || {};
		let b = args[1] || {};
		return Object.assign(a,b);
	}else if(args.length > 2){
		let a = args[0] || {};
		let b = args[1] || {};
		return obj.extend(Object.assign(a,b),...args.slice(2));
	}else{
		return args[0] || {};
	}
};

/**
 * json2string.
 * @public
 **/
 
obj.json2string = function(o){
	if(obj.istype(o,'undefined','function')) return null;
	if(obj.istype(o,'string','number','boolean') || !o) return o;
	try{
		return JSON.stringify(o);
	}catch(e){
		return '';
	}
}

/**
 * istype.
 * @public
 **/
 
obj.istype = function(o,...args){
	const t = typeof o;
	for(let one of args){
		if(t === one.toLowerCase()){
			return true;
		}
	}
	return false;
};

/**
 * string2json.
 * @public
 **/
 
obj.string2json = function(str){
	try{
		return JSON.parse(str);
	}catch(e){
		return str;
	}
};

/**
 * cryptoHash.
 * @public
 **/
 
obj.cryptoHash = function(str,type){
	if(!str) return null;
	return crypto.createHash(type).update(str).digest('hex');
};

/**
 * cryptoHamc.
 * @public
 **/
 
obj.cryptoHamc = function(str,key,type){
	if(!str || !key) return null;
	return crypto.createHmac(type,key).update(str).digest('hex');
};

/**
 * sha1.
 * @public
 **/
 
obj.sha1 = function(str){
	return obj.cryptoHash(str,'sha1');
};

/**
 * sha256.
 * @public
 **/
 
obj.sha256 = function(str){
	return obj.cryptoHash(str,'sha256');
};

/**
 * md5.
 * @public
 **/
 
obj.md5 = function(str){
	return obj.cryptoHash(str,'md5');
};

/**
 * encode.
 * @public
 **/
 
obj.encode = function(s){
	return encodeURIComponent(s);
};

/**
 * decode.
 * @public
 **/
 
obj.decode = function(s){
	return decodeURIComponent(s);
};

/**
 * escape.
 * @public
 **/
 
obj.escape = function(s){
	return escape(s);
};

/**
 * unescape.
 * @public
 **/
 
obj.unescape = function(s){
	return unescape(s);
};

/**
 * trim.
 * @public
 **/
 
obj.trim = function(text){
	if(!text) return "";
	return ( text + "" ).replace( /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "" );
};

/**
 * date.
 * @public
 **/
 
obj.date = function(...args){
	let [aDate,fmt] = [new Date(),'yyyy-MM-dd hh:mm:ss'];
	if(args.length>0){
		if(obj.istype(args[0],'string')){
			fmt = args[0];
		}else if(args[0] instanceof Date){
			aDate = args[0];
			if(args[1]) fmt = args[1];
		}
	}
	let o = {
		"M+" : aDate.getMonth()+1, 
		"d+" : aDate.getDate(), 
		"h+" : aDate.getHours()%12 == 0 ? 12 : aDate.getHours()%12, 
		"H+" : aDate.getHours(), 
		"m+" : aDate.getMinutes(), 
		"s+" : aDate.getSeconds(), 
		"q+" : Math.floor((aDate.getMonth()+3)/3), 
		"S" : aDate.getMilliseconds() 
	};
	let week = {
		"0" : "/u65e5",
		"1" : "/u4e00",
		"2" : "/u4e8c",
		"3" : "/u4e09",
		"4" : "/u56db",
		"5" : "/u4e94",
		"6" : "/u516d"
	};
	if(/(y+)/.test(fmt)){
		fmt=fmt.replace(RegExp.$1, (aDate.getFullYear()+"").substr(4 - RegExp.$1.length));
	}
	if(/(E+)/.test(fmt)){
		fmt=fmt.replace(RegExp.$1, ((RegExp.$1.length>1) ? (RegExp.$1.length>2 ? "/u661f/u671f" : "/u5468") : "")+week[aDate.getDay()+""]);
	}
	for(let k in o){
		if(new RegExp("("+ k +")").test(fmt)){
			fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
		}
	}
	return fmt;
};

/**
 * mkdirs.
 * @public
 **/
 
obj.mkdirs = function(dirpath) {
	let exists;
	try{
		fs.accessSync(dirpath);
		exists = true;
	}catch(err){
		exists = false;
	}
	if(!exists){
		let dirname = path.dirname(dirpath);
		obj.mkdirs(dirname);
		fs.mkdirSync(dirpath);
	}
};

/**
 * params2json.
 * @public
 **/
 
obj.params2json = function(s){
	let ret = {};
	if(obj.istype(s,'string')){
		let arr1 = s.replace(/^\?/g,'').split('&');
		if(arr1 && arr1.length){
			for(let i=0,len=arr1.length;i<len;i++){
				let arr2 = arr1[i]?arr1[i].split('='):[];
				if(arr2 && arr2.length && arr2.length ===2){
						ret[arr2[0]] = arr2[1];
				}
			}
		}
	}
	return ret;
};

/**
 * json2params.
 * @public
 **/
 
obj.json2params = function(o){
	let ret = '';
	if(obj.istype(o,'object')){
		let arr1 = [];
		let keys = Object.keys(o);
		for(let key of keys){
			const v = obj.encode(o[key]);
			arr1.push(key + '=' + v);
		}
		if(arr1.length>0){
			ret = arr1.join('&');
		}
	}
	return ret;
};

/**
 * REG.
 * @public
 **/
obj.REG = {
	NUMBER:/^[0-9]+$/
	,MOBILE:/^1[34578][0-9]{9}$/
	,CARD15:/^([1-9]\d{5})(\d{2})(0[1-9]|10|11|12)([0-2][1-9]|10|20|30|31)(\d{3})$/
	,CARD18:/^([1-9]\d{5})((19|20|21)\d{2})(0[1-9]|10|11|12)([0-2][1-9]|10|20|30|31)(\d{3})([0-9Xx])$/
	,URL: /((http|ftp|https):\/\/)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(\/[a-zA-Z0-9\&%_\.\/-~-]*)?/i
};
/**
 * card.
 * @public
 **/
obj.card = {
	validLast:function (str){
		return obj.card.getValid(str) === str[str.length-1];
	}
	,getValid:function (str){
		let m = 0
		let co=[7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2];
		let verCode=['1','0','X','9','8','7','6','5','4','3','2'];
		for (let i = 0; i < str.length-1; i++) {
				m+=parseInt(str[i])*co[i];
		}
		let residue=m%11;
		return verCode[residue];
	}
	,card15To18:function (str){
		if(!obj.REG.CARD15.test(str)) return str;
		let strs = str.split(obj.REG.CARD15);
		let year = parseInt(strs[2]);
		let nowyear = new Date().getFullYear() % 100;
		let beforeyear = '20';
		if(year > nowyear) beforeyear = '19';
		strs.splice(2,0,beforeyear);
		let card18 = strs.slice(0,7).join('');
		card18 += obj.card.getValid(card18 + '0');
		return card18;
	}
	,getCardInfo:function (str){
		let card = obj.card.card15To18(str);
		if(obj.REG.CARD18.test(card)){
			let cards = card.split(obj.REG.CARD18);
			cards.splice(3,1);
			let result = {
				area: cards[1]
				,sex: (parseInt(cards[5]) % 2 === 0? '女':'男')
				,birthday: cards.slice(2,5).join('-')
			};
			return result;
		}
	}
};